﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data.Objects;
using GlobalObjects;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Data.Entity.Infrastructure;
using WCFServiceWebRole1.DataObjects;
using System.ServiceModel.Web;
using System.Net;

namespace DataAccessLayer
{
    public class UserEntity{
     
        /// <summary>
        /// returns the user id based on the given sensor id
        /// </summary>
        /// <param name="sensorId">sensor id</param>
        /// <returns>user id</returns>
        public int getUserIdGivenSensorId_DAL(int sensorId)
        {

            using (MSR2DBEntities context = new MSR2DBEntities())
            {
                var deviceIdquery = from s in context.sensors
                                    where s.id == sensorId
                                    select s;
                if (deviceIdquery.Count() > 0)
                {
                    int deviceId = deviceIdquery.First().device_id.Value;
                    var userIdquery = from d in context.devices
                                      where d.id == deviceId
                                      select d;
                    if (userIdquery.Count() > 0)
                    {
                        return userIdquery.First().user_id.Value;
                    }
                    else
                    {
                        return 0;
                    }
                }

                else
                {
                    return 0;
                }
            }

        }
        /// <summary>
        /// returns the user id of the user with the specified username
        /// </summary>
        /// <param name="username">username</param>
        /// <returns>user id</returns>
        public int getUserIdByUsername_DAL(string username) { 
            int userId = 0;
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                var query = from u in ctx.users
                            where u.username == username
                            select u;
                if (query.Count() == 1)
                {
                    foreach (var u in query)
                    {
                        userId = u.id;
                    }
                }
                else {
                    DbUpdateException exc = new DbUpdateException("username doesn't exist");
                    throw exc;
                }
            }
            return userId;
        }
        /// <summary>
        /// checks if the given username and password represent a valid user
        /// </summary>
        /// <param name="username">username of the user</param>
        /// <param name="password">password of the user</param>
        /// <returns>the id of the corresponding user. 0 if doesnt exist</returns>
        public int checkLogin_DAL(string username,string password) {
            int isCorrect = 0;
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                var query = from u in ctx.users
                            where u.username == username
                            select u;
                if (query.Count() == 1)
                {
                    var userx = query.First();
                    string salt = userx.salt;
                    string hash = encrypt(password, salt);
                    if (hash.Equals(userx.hash))
                    {
                        isCorrect = userx.id;
                    }
                }
            }

            return isCorrect;
        }
        /// <summary>
        /// change the password of the given user
        /// </summary>
        /// <param name="id">user id</param>
        /// <param name="oldPassword">old password</param>
        /// <param name="newPassword">new password</param>
        /// <returns>true if the password has been changed successfully</returns>
        public Boolean changePassword(int id, string oldPassword,string newPassword){
            Boolean result = false;
            Boolean passOk = false;
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                var query = from u in ctx.users
                            where u.id == id
                            select u;
                if (query.Count() == 1)
                {
                    var userx = query.First();
                    string salt = userx.salt;
                    string hash = encrypt(oldPassword, salt);
                    if (hash.Equals(userx.hash))
                    {
                        passOk = true;
                    }
                    if (passOk)
                    {
                        string saltValue = createSalt(newPassword.Length);
                        string newHash = encrypt(newPassword, saltValue);
                        userx.salt = saltValue;
                        userx.hash = newHash;
                        ctx.SaveChanges();
                        result = true;
                    }
                }
                else
                {
                    InvalidOperationException exc = new InvalidOperationException();
                    throw exc;
                }
            }
            return result;
        }

        /// <summary>
        /// deletes the user from the database based on the given user id and password
        /// </summary>
        /// <param name="id"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public Boolean deleteUser_DAL(int id,String password){
        Boolean result = false;
        Boolean passOk = false; 
        using (MSR2DBEntities ctx = new MSR2DBEntities())
        {

            var query = from u in ctx.users
                        where u.id == id
                        select u;
            if (query.Count() == 1)
            {
                var userx = query.First();
                string salt = userx.salt;
                string hash = encrypt(password, salt);
                if (hash.Equals(userx.hash))
                {
                    passOk = true;
                }
                if (passOk)
                {
                    var query1 = from d in ctx.devices
                                 where d.user_id == id
                                 select d;
                    if (query1.Count() != 0)
                    {
                        foreach (var dev in query1)
                        {
                            var query3 = from s in ctx.sensors
                                         where s.device_id == dev.id
                                         select s;
                            if (query3.Count() != 0)
                            {
                                foreach (var sen in query3)
                                {
                                    //
                                    var queryAccess = from a in ctx.sensor_access_time
                                                 where a.sensor_id == sen.id
                                                 select a;
                                    if (queryAccess.Count() != 0)
                                    {
                                        ctx.sensor_access_time.Remove(queryAccess.First());
                                    }
                                    //
                                    ctx.sensors.Remove(sen);
                                }
                            }
                            ctx.devices.Remove(dev);
                        }
                    }
                    //
                    var query2 = from s in ctx.shareds
                                 where s.user_id == id
                                 select s;
                    if (query2.Count() != 0)
                    {
                        foreach (var shar in query2)
                        {
                            ctx.shareds.Remove(shar);
                        }
                    }
                    //
                    var query4 = from n in ctx.notifications
                                 where n.user_id == id
                                 select n;
                    if (query4.Count() != 0)
                    {
                        foreach (var not in query4)
                        {
                            ctx.notifications.Remove(not);
                        }
                    }
                    //
                    ctx.users.Remove(userx);
                    ctx.SaveChanges();
                    result = true;
                }
            }
            else {
                InvalidOperationException exc = new InvalidOperationException();
                throw exc;
            }
        }
        return result;
        }

        /// <summary>
        /// returns the details of the specified user.
        /// </summary>
        /// <param name="id">user's id</param>
        /// <returns>details of the user</returns>
        public UserDetails getUserById_DAL(int id) {
            UserDetails userdetails = new UserDetails();
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                    var query = from u in ctx.users
                                where u.id == id
                                select u;
                    if (query.Count()!=0)
                    {
                        var userx = query.First();
                        userdetails.Id = userx.id;
                        userdetails.Username = userx.username;
                        userdetails.Title = userx.title;
                        userdetails.FirstName = userx.firstName;
                        userdetails.LastName = userx.lastName;
                        userdetails.Organization = userx.organisation;
                        userdetails.EmailAddress = userx.email;
                        userdetails.Country = userx.country;
                        userdetails.Purpose = userx.purpose;
                    }
                    else
                    {
                      //  ErrorData errordata = new ErrorData("User ID does not exist.");
                      //  throw new WebFaultException<ErrorData>(errordata, HttpStatusCode.NotFound);
                         InvalidOperationException exc =  new InvalidOperationException();
                         throw exc;
                    }    
            }
            return userdetails;
        }
        /// <summary>
        /// updates the information of the specified user
        /// </summary>
        /// <param name="id">specified user</param>
        /// <param name="userUpdated"></param>
        /// <returns>updated information</returns>
        public UserDetails updateUserById_DAL(int id, User userUpdated) {
            UserDetails userdetails = new UserDetails();
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                var query = from u in ctx.users
                            where u.id == id
                            select u;
                if (query.Count()!=0)
                {
                    var userx = query.First();
                    if (userUpdated.Username != null )
                    {
                        if (userx.username != userUpdated.Username)
                        {
                            ErrorData errordata = new ErrorData("Username cannot be changed");
                            InvalidOperationException exc = new InvalidOperationException();
                            throw exc;
                        }
                        else
                        {
                            userdetails.Id = userx.id;
                            userdetails.Username = userx.username;
                        }
                    }
                    else {
                        userdetails.Id = userx.id;
                        userdetails.Username = userx.username;
                    }
                    if (userUpdated.Title != null)
                    {
                        userdetails.Title = userUpdated.Title;
                        userx.title = userUpdated.Title;
                    }
                    else {
                        userdetails.Title = userx.title;
                    }
                    if (userUpdated.FirstName != null)
                    {
                        userdetails.FirstName = userUpdated.FirstName;
                        userx.firstName = userUpdated.FirstName;
                    }
                    else {
                        userdetails.FirstName = userx.firstName;
                    }
                    if (userUpdated.LastName != null)
                    {
                        userdetails.LastName = userUpdated.LastName;
                        userx.lastName = userUpdated.LastName;
                    }
                    else{
                        userdetails.LastName = userx.lastName;
                    }
                    if (userUpdated.Organisation != null)
                    {
                        userdetails.Organization = userUpdated.Organisation;
                        userx.organisation = userUpdated.Organisation;
                    }
                    else{
                        userdetails.Organization = userx.organisation;
                    }
                    if (userUpdated.Email != null)
                    {
                        userdetails.EmailAddress = userUpdated.Email;
                        userx.email = userUpdated.Email;
                    }
                    else
                    {
                        userdetails.EmailAddress = userx.email;
                    }
                    if (userUpdated.Country != null)
                    {
                        userdetails.Country = userUpdated.Country;
                        userx.country = userUpdated.Country;
                    }
                    else
                    {
                        userdetails.Country = userx.country;
                    }
                    if (userUpdated.Purpose != null)
                    {
                        userdetails.Purpose = userUpdated.Purpose;
                        userx.purpose = userUpdated.Purpose;
                    }
                    else
                    {
                        userdetails.Purpose = userx.purpose;
                    }
                    ctx.SaveChanges();
                }
                else
                {
                  //  ErrorData errordata = new ErrorData("User ID does not exist.");
                  //  throw new WebFaultException<ErrorData>(errordata, HttpStatusCode.NotFound);
                 InvalidOperationException exc= new InvalidOperationException();
                            throw exc;
                }
            }
            return userdetails;
        }

        /// <summary>
        /// Creates a salt value to use in encryption
        /// </summary>
        /// <param name="size"></param>
        /// <returns>salt value</returns>
        private string createSalt(int size)
        {
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] buff = new byte[size];
            rng.GetBytes(buff);
            return Convert.ToBase64String(buff);
        }
        
        /// <summary>
        /// encrypts the password using Sha256 by using the salt value
        /// </summary>
        /// <param name="password">user password</param>
        /// <param name="salt">salt value</param>
        /// <returns>encrypted password</returns>
        public string encrypt(string password,string salt) {
            HashAlgorithm hashAlg = new SHA256CryptoServiceProvider();
            byte[] bytValue = System.Text.Encoding.UTF8.GetBytes(salt + password);
            byte[] bytHash = hashAlg.ComputeHash(bytValue);
            string base64 = Convert.ToBase64String(bytHash);
            return base64;
        }
        
        /// <summary>
        /// Adds new user to the database and returns the created user.
        /// </summary>
        /// <param name="newUser">the user that will be added</param>
        /// <returns>the user that was added to users table</returns>
        public User addUser_DAL(User newUser)
        {
            try
            {
                // create a salt
                string saltValue = createSalt(newUser.Password.Length);
                // hash the password with salt
                string hashedPassword = encrypt(newUser.Password, saltValue);
                User createdUser = new User();
                createdUser.Password = newUser.Password;
                using (var context = new MSR2DBEntities())
                {
                    var nuser = new user()
                    {
                        username = newUser.Username,
                        title = newUser.Title,
                        firstName = newUser.FirstName,
                        lastName = newUser.LastName,
                        organisation = newUser.Organisation,
                        email = newUser.Email,
                        country = newUser.Country,
                        purpose = newUser.Purpose,
                        salt = saltValue,
                        hash = hashedPassword
                    };
                    context.users.Add(nuser);
                    context.SaveChanges();

                    createdUser.Username = nuser.username;
                    createdUser.Password = newUser.Password;
                    createdUser.Title = nuser.title;
                    createdUser.FirstName = nuser.firstName;
                    createdUser.LastName = nuser.lastName;
                    createdUser.Organisation = nuser.organisation;
                    createdUser.Email = nuser.email;
                    createdUser.Country = nuser.country;
                    createdUser.Purpose = nuser.purpose;
                    createdUser.Id = nuser.id;
                    Console.WriteLine("New user is added successfully- ID is:" + nuser.id);
                    return createdUser;
                }
            }
            catch (DbUpdateException sqle)
            {
                Console.WriteLine("Error in addUser");
                Debug.Print("Error in addUser(), "+sqle.ToString());
                throw sqle;
            }
        }
        /// <summary>
        /// checks whether the given username exist in the database or not
        /// </summary>
        /// <param name="username">new username to be checked for availability</param>
        /// <returns>true if the username already exist, false if it doesn't exist</returns>
        public Boolean checkUsernameDAL(String username) {
            bool exist =false;
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                 foreach (user user in ctx.users)
                {
                    if(user.username.Equals(username)){
                        exist = true;
                    }
                }
            }
            return exist;
        }
        public Boolean checkUserIdDAL(int userid)
        {
            bool exist = false;
            using (MSR2DBEntities ctx = new MSR2DBEntities())
            {
                foreach (user user in ctx.users)
                {
                    if (user.id.Equals(userid))
                    {
                        exist = true;
                    }
                }
            }
            return exist;
        }
    }
  
}

